/*!

@author Sergio J. de los Santos
*/
#include <stdafx.h>

#include "SystemEvents.h"
#include "Direct3D.h"

GraphicDeviceD3D11* g_pInstance = NULL;

//! Constructor
GraphicDeviceD3D11::GraphicDeviceD3D11()
{
	m_hInstance	= GetModuleHandle(NULL);
	g_pInstance	= this;
	m_hWnd		= NULL;

	m_pd3dDevice			= NULL;
	m_pd3dImmediateContext	= NULL;
	m_pdxgiSwapChain		= NULL;
	m_pd3dSamplerLinear		= NULL;
}

//! Destructor
GraphicDeviceD3D11::~GraphicDeviceD3D11()
{
	if(m_pd3dDevice)
		Finalize();
	g_pInstance = NULL;
}

/*!

@param
@param
@return 
*/
HRESULT GraphicDeviceD3D11::CreateEngineWindow(int& width, int& height, bool fullscreen)
{
	HRESULT		hr				= S_OK;
	UINT32		dwStyleWindow	= WS_POPUP | WS_VISIBLE;

	if (!fullscreen)
	{
		dwStyleWindow	|= WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CAPTION | WS_SYSMENU;
		RECT r;
		r.left = 0;	r.right = width;
		r.top = 0; r.bottom = height;
		DWORD dwWindowFlags		= WS_CAPTION|WS_MINIMIZEBOX|WS_SYSMENU|WS_POPUP|WS_MAXIMIZEBOX;
		DWORD dwExtendedFlags	= WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;			// Window Extended Style
		//AdjustWindowRectEx(&r, dwWindowFlags, FALSE, dwExtendedFlags);
		width = r.right - r.left;
		height = r.bottom - r.top;
	}

	// Set up window attributes
	WNDCLASSEX	wndclass;
	wndclass.cbSize			= sizeof(wndclass);
	wndclass.style			= CS_OWNDC;
	wndclass.lpfnWndProc	= (WNDPROC) GraphicDeviceD3D11::WinProcStatic;
	wndclass.cbClsExtra		= 0;
	wndclass.cbWndExtra		= 0;
	wndclass.hInstance		= m_hInstance;
	wndclass.hIcon			= NULL;
	wndclass.hCursor		= NULL;
	wndclass.hbrBackground	= NULL;
	wndclass.lpszMenuName	= NULL;
	wndclass.lpszClassName	= L"Terrain Demo";	// Registered Class Name
	wndclass.hIconSm		= NULL;

	//register our new window class with the Windows OS
	if( RegisterClassEx( &wndclass ) == 0 ) 
		return E_FAIL;


	m_hWnd = CreateWindow( L"Terrain Demo", L"Terrain Demo", dwStyleWindow, 0, 0, width, height, NULL, NULL, m_hInstance, this );
	if (!m_hWnd)
		return E_FAIL;

	ShowWindow(m_hWnd, SW_SHOWDEFAULT);

	UpdateWindow(m_hWnd);
	return hr;
}

/*! Windows Callback Function

*/
LRESULT CALLBACK GraphicDeviceD3D11::WinProcStatic(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	return SystemEvents::WinEventHandler(hWnd,msg,wParam,lParam);
}


/*! Initialize the graphic device

@param width		width of the window/video mode
@param height		height of the window/video mode
@param fullscreen	is fullscreen?
@return true if ok, false if failed
*/
bool GraphicDeviceD3D11::Initialize(int width, int height, bool fullscreen)
{

	if(FAILED(CreateEngineWindow(width, height, fullscreen)))
		return false;

	RECT rect;
	GetClientRect(m_hWnd, &rect);
	width	= rect.right - rect.left;
	height	= rect.bottom - rect.top;

	// Swap Chain Config	
	ZeroMemory( &m_descSwapChain, sizeof(m_descSwapChain) );
	m_descSwapChain.BufferCount				= 1;
	m_descSwapChain.BufferDesc.Width		= width;
	m_descSwapChain.BufferDesc.Height		= height;
	m_descSwapChain.BufferDesc.Format		= DXGI_FORMAT_R8G8B8A8_UNORM;//_SRGB;
	m_descSwapChain.BufferDesc.RefreshRate.Numerator	= 60;//fullscreen?60:0;
	m_descSwapChain.BufferDesc.RefreshRate.Denominator	= 1;
	m_descSwapChain.BufferUsage				= DXGI_USAGE_RENDER_TARGET_OUTPUT;
	m_descSwapChain.OutputWindow			= m_hWnd;
	m_descSwapChain.SampleDesc.Count		= 1;
	m_descSwapChain.SampleDesc.Quality		= 0;
	m_descSwapChain.Windowed				= !fullscreen;

	UINT uiCreateDeviceFlags = 0;//D3D11_CREATE_DEVICE_DEBUG;//D3D11_CREATE_DEVICE_SINGLETHREADED;

	D3D_FEATURE_LEVEL featureLevels[] = 
	{
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0
	};

	D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_10_0;

	HRESULT hr = D3D11CreateDeviceAndSwapChain( 
		NULL, 
		D3D_DRIVER_TYPE_HARDWARE, 
		NULL, 
		uiCreateDeviceFlags, 
		featureLevels, 
		sizeof(featureLevels)/sizeof(D3D_FEATURE_LEVEL),
		D3D11_SDK_VERSION, 
		&m_descSwapChain, 
		&m_pdxgiSwapChain, 
		&m_pd3dDevice, 
		&featureLevel, 
		&m_pd3dImmediateContext );
	if( FAILED(hr) )
		return false;



	// Create a render target view
	ID3D11Texture2D *pBackBuffer;
	hr = m_pdxgiSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (LPVOID*)&pBackBuffer );
	if( FAILED(hr) )
		return false;

	hr = m_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &m_pd3dRenderTargetView );
	D3D11_TEXTURE2D_DESC			texDescBB;
	pBackBuffer->GetDesc(&texDescBB);
	pBackBuffer->Release();
	if( FAILED(hr) )
		return false;

	D3D11_DEPTH_STENCIL_VIEW_DESC	m_dsViewDesc;	// Desc si es Depth Stencil Buffer
	D3D11_TEXTURE2D_DESC			texDesc;

	texDesc.MipLevels			= 1;
	texDesc.ArraySize			= 1;
	texDesc.Format				= DXGI_FORMAT_R24G8_TYPELESS;
	texDesc.SampleDesc.Count	= 1;
	texDesc.SampleDesc.Quality	= 0;
	texDesc.Usage				= D3D11_USAGE_DEFAULT;
	texDesc.BindFlags			= D3D11_BIND_DEPTH_STENCIL; 
	texDesc.CPUAccessFlags		= 0;
	texDesc.MiscFlags			= 0;
	texDesc.Width				= texDescBB.Width;
	texDesc.Height				= texDescBB.Height;

	ID3D11Resource*					m_pd3dTexture;
	// Create Texture
	hr = m_pd3dDevice->CreateTexture2D( &texDesc, NULL, (ID3D11Texture2D**)&m_pd3dTexture );
	if( FAILED(hr) )
		return false;

	// Create the depth stencil view
	ZeroMemory(&m_dsViewDesc, sizeof(m_dsViewDesc));
	m_dsViewDesc.Format				= DXGI_FORMAT_D24_UNORM_S8_UINT;
	m_dsViewDesc.ViewDimension		= D3D11_DSV_DIMENSION_TEXTURE2D;
	m_dsViewDesc.Texture2D.MipSlice	= 0;
	hr = m_pd3dDevice->CreateDepthStencilView( m_pd3dTexture, &m_dsViewDesc, &m_pd3dDepthStencilView );
	if( FAILED(hr) )
		return false;

	_SAFE_RELEASE(m_pd3dTexture);

#ifdef DEBUG
	m_pd3dDepthStencilView->SetPrivateData( WKPDID_D3DDebugObjectName, sizeof( "RenderTargetDepthStencil" ) - 1, "RenderTargetDepthStencil" );
	m_pd3dTexture->SetPrivateData( WKPDID_D3DDebugObjectName, sizeof( "RenderTargetDepthStencil" ) - 1, "RenderTargetDepthStencil" );
#endif 

	Present();

	// Create and set the sampler

	D3D11_SAMPLER_DESC  descSampLinear;
	descSampLinear.Filter			= D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
	descSampLinear.AddressU			= D3D11_TEXTURE_ADDRESS_WRAP;
	descSampLinear.AddressV			= D3D11_TEXTURE_ADDRESS_WRAP;
	descSampLinear.AddressW			= D3D11_TEXTURE_ADDRESS_WRAP;
	descSampLinear.MipLODBias		= 0;
	descSampLinear.MaxAnisotropy	= 1;
	descSampLinear.ComparisonFunc	= D3D11_COMPARISON_NEVER;
	descSampLinear.BorderColor[0]	= 0;
	descSampLinear.BorderColor[1]	= 0;
	descSampLinear.BorderColor[2]	= 0;
	descSampLinear.BorderColor[3]	= 0;
	descSampLinear.MinLOD			= 0;
	descSampLinear.MaxLOD			= D3D11_FLOAT32_MAX;


	m_pd3dDevice->CreateSamplerState(&descSampLinear, &m_pd3dSamplerLinear);
	m_pd3dImmediateContext->PSSetSamplers(0, 1, &m_pd3dSamplerLinear);
	m_pd3dImmediateContext->VSSetSamplers(0, 1, &m_pd3dSamplerLinear);
	m_pd3dImmediateContext->GSSetSamplers(0, 1, &m_pd3dSamplerLinear);


	D3D11_VIEWPORT d3dViewport;
	d3dViewport.Width		= (float)width;
	d3dViewport.Height		= (float)height;
	d3dViewport.MinDepth	= 0;
	d3dViewport.MaxDepth	= 1;
	d3dViewport.TopLeftX	= 0;
	d3dViewport.TopLeftY	= 0;

	m_pd3dImmediateContext->RSSetViewports(1, &d3dViewport);
	return true;
}

//! Finalize the graphic device
void GraphicDeviceD3D11::Finalize()
{
	_SAFE_RELEASE(m_pd3dSamplerLinear);
	_SAFE_RELEASE(m_pd3dRenderTargetView);
	_SAFE_RELEASE(m_pd3dDepthStencilView);
	_SAFE_RELEASE(m_pd3dImmediateContext);
	_SAFE_RELEASE(m_pdxgiSwapChain);
	_SAFE_RELEASE(m_pd3dDevice);
}